local mod = SexyCooldown2:NewModule("Spells", "AceEvent-3.0")
local L = LibStub("AceLocale-3.0"):GetLocale("SexyCooldown2")
local LibICD = LibStub("LibInternalCooldowns-1.0")

local aniFrame = CreateFrame("Frame")
local spells = { PLAYER = {}, PET = {} }
local lastPlayerSpell, lastPetSpell = {}, {}

local function cacheSpellsForBook(t, book)
	wipe(t)
	for x = 1, GetNumSpellTabs() do
		local tname, texture, offset, numSpells, isGuild = GetSpellTabInfo(x)

		for i = (offset + 1), (offset + numSpells - 1) do
			local name = GetSpellBookItemName(i, book)

			if name then
				local link = GetSpellLink(i, book)

				if link then
					local id = tonumber(link:match("spell:(%d+)"))

					if id and id > 0 then
						t[name] = id
					end
				end

			else
				do break end
			end
		end
	end
end

function mod:OnInitialize()
	SexyCooldown2.RegisterFilter(self, "SPELL_COOLDOWN",
		L["My spells"],
		L["Show my spell cooldowns."])

	SexyCooldown2.RegisterFilter(self, "PET_SPELL_COOLDOWN",
		L["Pet spells"],
		L["Show my pet's cooldowns."])

	SexyCooldown2.RegisterFilter(self, "INTERNAL_SPELL_COOLDOWN",
		L["Internal cooldowns"],
		L["Show the internal cooldowns of my spells and talents."])

	aniFrame.timer = aniFrame:CreateAnimationGroup()
	aniFrame[1] = aniFrame.timer:CreateAnimation()
	aniFrame[1]:SetDuration(0.1)
	aniFrame[2] = aniFrame.timer:CreateAnimation()
	aniFrame[2]:SetDuration(0)
	aniFrame[2]:SetOrder(2)
	aniFrame[2]:SetStartDelay(0.75)
	aniFrame[2]:SetScript("OnPlay", function()
		mod:Refresh()
	end)
	aniFrame[2]:SetScript("OnFinished", function()
		mod:CleanupCooldowns()
	end)
end

function mod:OnEnable()
	self:CacheSpells()
	self:RegisterEvent("SPELL_UPDATE_COOLDOWN")
	self:RegisterEvent("SPELLS_CHANGED", "CacheSpells")
	self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
	self:RegisterEvent("UNIT_SPELLCAST_FAILED")
	LibICD.RegisterCallback(self, "InternalCooldowns_TalentProc")
	self:Refresh()
end

function mod:InternalCooldowns_TalentProc(callback, spellID, start, duration)
	local name, _, icon = GetSpellInfo(spellID)
	local uid = ("%s:%d"):format("spell", spellID)
	SexyCooldown2:AddItem(uid, name, icon, start, duration, nil, "INTERNAL_SPELL_COOLDOWN", SexyCooldown2.SHOW_HYPERLINK, "spell:" .. spellID)
end

function mod:UNIT_SPELLCAST_FAILED(event, unit, spell, rank)
	if unit == "player" and spells.PLAYER[spell] then
		SexyCooldown2:CastFailure("spell:" .. spells.PLAYER[spell])

	elseif unit == "pet" and spells.PET[spell] then
		SexyCooldown2:CastFailure("spell:" .. spells.PET[spell])
	end
end

function mod:SPELL_UPDATE_COOLDOWN()
	if aniFrame.timer:IsPlaying() then
		aniFrame.timer:Stop()
	end

	aniFrame.timer:Play()
end

function mod:UNIT_SPELLCAST_SUCCEEDED(event, unit, spell)
	if unit == "player" then
		tinsert(lastPlayerSpell, 2, spell)

		if #lastPlayerSpell > 2 then
			tremove(lastPlayerSpell)
		end

	elseif unit == "pet" then
		tinsert(lastPetSpell, 2, spell)

		if #lastPetSpell > 2 then
			tremove(lastPetSpell)
		end
	end
end

function mod:Refresh()
	mod:UpdateSpellCooldowns(lastPlayerSpell, spells.PLAYER, "SPELL_COOLDOWN")

	if HasPetUI() then
		mod:UpdateSpellCooldowns(lastPetSpell, spells.PET, "PET_SPELL_COOLDOWN")
	end
end

function mod:CleanupCooldowns()
	mod:CleanupSpellCooldowns(lastPlayerSpell, spells.PLAYER, "SPELL_COOLDOWN")

	if HasPetUI() then
		mod:CleanupSpellCooldowns(lastPetSpell, spells.PET, "PET_SPELL_COOLDOWN")
	end
end

local function getID(name)
	local link = GetSpellLink(name)

	if link then
		return link:match("spell:(%d+)")
	end

	return nil
end

function mod:UpdateSpellCooldowns(spellQueue, spellSet, filter)
	local start, duration, active, id
	local added = false

	for _, name in ipairs(spellQueue) do
		start, duration, active = GetSpellCooldown(name)

		if active == 1 and start > 0 and duration > 2 then
			local id = spellSet[name] or getID(name)
			local _, _, icon = GetSpellInfo(id)
			local uid = "spell:" .. id
			SexyCooldown2:AddItem(uid, name, icon, start, duration, nil, filter, SexyCooldown2.SHOW_HYPERLINK, uid)
			added = true
		end
	end

	if not added then
		for name, id in pairs(spellSet) do
			start, duration, active = GetSpellCooldown(name)

			if active == 1 and start > 0 and duration > 2 then
				local _, _, icon = GetSpellInfo(id)
				local uid = "spell:" .. id
				SexyCooldown2:AddItem(uid, name, icon, start, duration, nil, filter, SexyCooldown2.SHOW_HYPERLINK, uid)
			end
		end
	end
end

function mod:CleanupSpellCooldowns(spellQueue, spellSet, filter)
	for name, id in pairs(spellSet) do
		local start, duration, active = GetSpellCooldown(name)

		if start == 0 then
			SexyCooldown2:RemoveItem("spell:" .. id)
		end
	end
end

function mod:CacheSpells()
	cacheSpellsForBook(spells.PLAYER, BOOKTYPE_SPELL)
	cacheSpellsForBook(spells.PET, BOOKTYPE_PET)
end